package com.yy.job.service.impl;

import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.alibaba.fastjson.JSON;
import com.google.common.base.Preconditions;
import com.yy.job.common.dao.YYJobDOExtMapper;
import com.yy.job.common.dao.YYJobDOMapper;
import com.yy.job.common.dao.YYJobStepDOExtMapper;
import com.yy.job.common.dao.po.YYJobDO;
import com.yy.job.common.dao.po.YYJobDOExample;
import com.yy.job.common.dao.po.YYJobLogDO;
import com.yy.job.common.dao.po.YYJobStepDO;
import com.yy.job.common.dao.po.YYJobStepLogDO;
import com.yy.job.common.enums.ErrorCodeEnum;
import com.yy.job.common.enums.JobStatusEnum;
import com.yy.job.common.enums.JobStepStatusEnum;
import com.yy.job.common.service.impl.YYJobLogServiceImpl;
import com.yy.job.common.service.impl.YYJobServiceImpl;
import com.yy.job.common.service.impl.YYJobStepLogServiceImpl;
import com.yy.job.common.service.impl.YYJobStepServiceImpl;
import com.yy.job.core.domain.JobDomain;
import com.yy.job.core.domain.JobStepDomain;
import com.yy.job.core.domain.JobStepExecLogDomain;
import com.yy.job.core.domain.context.JobContext;
import com.yy.job.core.domain.context.JobStepContext;
import com.yy.job.core.domain.converter.JobDomainConverter;
import com.yy.job.core.domain.converter.JobStepExecLogConverter;
import com.yy.job.service.IJobManager;
import com.yy.job.utils.ThrowableUtils;
import com.yy.job.utils.exception.YYJobRuntimeExceptionUtils;

@Service
public class JobManager implements IJobManager {

	private Logger log = LoggerFactory.getLogger(JobManager.class);

	@Autowired
	private YYJobServiceImpl queueJobServiceImpl;

	@Autowired
	private YYJobStepServiceImpl queueJobTaskServiceImpl;

	@Autowired
	private YYJobStepLogServiceImpl queueJobTaskExecLogServiceImpl;
	
	@Autowired
	private YYJobDOMapper jobDOMapper;
	
	@Autowired
	private YYJobDOExtMapper queueJobDOExtMapper;
	
	@Autowired
	private JobStepDomainService JobStepDomainService;
	
	@Autowired
	private YYJobStepDOExtMapper jobStepDOExtMapper;
	
	@Autowired 
	private YYJobLogServiceImpl yyJobLogServiceImpl;
	
	@Override
	@Transactional
	public void startJobExecute(JobContext jobContext) {
		// 保存job_log日志
		YYJobLogDO jobLogDO = new YYJobLogDO();
		jobLogDO.setJobId(jobContext.getJobDomain().getId());
		jobLogDO.setJobAction(jobContext.getJobAction().getCode());
		jobLogDO.setExecStartTime(jobContext.getExecStartTime());
		Date nowDate = new Date();
		jobLogDO.setGmtCreated(nowDate);
		jobLogDO.setGmtModified(nowDate);
		jobLogDO.setCreator(jobContext.getOperator());
		jobLogDO.setModifier(jobContext.getOperator());
		
		Long jobLogId = this.yyJobLogServiceImpl.saveSelective(jobLogDO);
		jobContext.setJobLogId(jobLogId);
		
		// 增加执行次数和执行时间
		queueJobDOExtMapper.updateJobExecCountByID(jobContext.getJobDomain().getId(), jobContext.getOperator(), new Date());
	}
	
	@Override
	@Transactional
	public void endJobExecute(JobContext jobContext) {
		// 保存日志
		YYJobLogDO jobLogDO = new YYJobLogDO();
		jobLogDO.setId(jobContext.getJobLogId());
		jobLogDO.setExecEndTime(jobContext.getExecEndTime());
		jobLogDO.setErrMsg(ThrowableUtils.subStackTrace(jobContext.getErrorMsg(),500));
		jobLogDO.setErrStack(ThrowableUtils.subStackTrace(jobContext.getErrStack()));
		this.yyJobLogServiceImpl.saveSelective(jobLogDO);
		
		// 修改yy_job状态
		YYJobDO jobDO = new YYJobDO();
		jobDO.setId(jobContext.getJobDomain().getId());
		jobDO.setJobStatus(jobContext.getFinalJobStatus().getCode());
		jobDO.setModifier(jobContext.getOperator());
		jobDO.setGmtModified(new Date());
		this.jobDOMapper.updateByPrimaryKeySelective(jobDO);
	}
	
	@Override
	@Transactional
	public void startJobStepExecute(JobStepContext jobStepContext) {
		Preconditions.checkNotNull(jobStepContext.getJobStepDomain().getId());
		// 更新状态为R且修改执行次数+1
		jobStepDOExtMapper.updateJobStepExecCountByID(jobStepContext.getJobStepDomain().getId(), jobStepContext.getJobStepDomain().getOperator(), new Date());
	}
	
	@Override
	@Transactional
	public void endJobStepExecute(JobStepContext jobStepContext) {
		String operator = jobStepContext.getJobContext().getOperator();
		JobStepExecLogDomain stepExecLog = JobStepExecLogConverter.buildStepExecLog(jobStepContext.getJobContext(),
				jobStepContext);
		JobStepStatusEnum jobStepStatus = jobStepContext.getJobResult().isSuccess() ? JobStepStatusEnum.SUCCESS
			: JobStepStatusEnum.FAIL;
		// 保存日志
		this.saveJobStepExecResult(jobStepContext.getJobStepDomain(), stepExecLog);
		// 修改step状态
		this.queueJobTaskServiceImpl.updateStatusById(jobStepContext.getJobStepDomain().getId(), jobStepStatus.getCode(), operator);
	}
	
	@Override
	public Long saveJobStepExecResult(JobStepDomain jobStepDomain, JobStepExecLogDomain jobStepLogDomain) {
		// 保存日志
		YYJobStepLogDO jobTaskLogDO = jobStepLogDomain.convertToDO();
		this.queueJobTaskExecLogServiceImpl.saveSelective(jobTaskLogDO);

		return jobTaskLogDO.getId();
	}
	
	@Override
	public JobDomain queryJobByBusinessNo(String projectCode, String moduleCode, String jobCode, String businessNo) {
		Preconditions.checkNotNull(projectCode);
		Preconditions.checkNotNull(moduleCode);
		Preconditions.checkNotNull(jobCode);
		Preconditions.checkNotNull(businessNo);
		// 根据projectCode + moduleCode + jobCode + businessNo查询
		YYJobDO jobDO = this.queueJobServiceImpl.selectJobByBusinessNo(projectCode, moduleCode, jobCode, businessNo);
		if (jobDO == null) {
			return null;
		}
		
		JobDomain job = new JobDomain(jobDO);
		List<JobStepDomain> taskList = this.JobStepDomainService.listJobStepByJobId(job.getId());
		job.setJobStepDomains(taskList);
		return job;
	}
	
	@Override
	@Transactional
	public int lockJobByVersion(JobDomain jobDomain, String modifier) {
		Preconditions.checkArgument(jobDomain != null, "jobDomain is null");
		Preconditions.checkArgument(jobDomain.getId() != null, "id is null");
		
		YYJobDO row = new YYJobDO();
		row.setVersion(jobDomain.getVersion() + 1);
		row.setJobStatus(JobStatusEnum.LOCK.getCode());
		row.setModifier(modifier);
		row.setGmtModified(new Date());
		
		YYJobDOExample example = new YYJobDOExample();
		example.createCriteria().andIdEqualTo(jobDomain.getId()).andJobStatusEqualTo(jobDomain.getJobStatus().getCode())
				.andVersionEqualTo(jobDomain.getVersion()).andIsDeletedEqualTo("N");
		
		return this.jobDOMapper.updateByExampleSelective(row, example);
	}
	
	@Override
	@Transactional
	public Long submit(JobDomain newJob) {
		log.info("JobManager submit, param newJob={}", JSON.toJSONString(newJob));
		Preconditions.checkArgument(newJob != null, "job is null");
		
		// 避免重复提交
//		JobDomain oldJob = queryJobByBusinessNo(newJob.getProjectCode(),newJob.getModuleCode(), newJob.getJobCode(), newJob.getBusinessNo());
//		if(oldJob != null) {
//			YYJobRuntimeExceptionUtils.throwException(ErrorCodeEnum.SUBMIT_JOB_DUPLICATED);
//		}
		
		// 只支持新增，不支持修改和删除
		YYJobDO jobDO = JobDomainConverter.convertToDO(newJob);
		List<YYJobStepDO> taskDOList = newJob.getJobStepDomains() == null ? null : newJob.getJobStepDomains().stream().map(e->e.convertToDO()).collect(Collectors.toList());
		Long jobId = this.queueJobServiceImpl.saveCascade(jobDO, taskDOList);
		
		return jobId;
	}

}
